/****************************************************************************
 *  Copyright 2022 Gorgon Meducer (Email:embedded_zhuoran@hotmail.com)       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

#ifndef __PERF_COUNTER_H__
#define __PERF_COUNTER_H__

/*============================ INCLUDES ======================================*/
#include <macro.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "cmsis_compiler.h"

#ifdef __cplusplus
extern "C" {
#endif
/*============================ MACROS ========================================*/

/*!
 * \addtogroup gBasic 1 Basic
 * @{
 */
#define __PERF_COUNTER_VER_MAJOR__ 2
#define __PERF_COUNTER_VER_MINOR__ 2
#define __PERF_COUNTER_VER_REVISE__ 4

#define __PERF_COUNTER_VER_STR__ ""

#define __PER_COUNTER_VER__                 \
    (__PERF_COUNTER_VER_MAJOR__ * 10000ul + \
     __PERF_COUNTER_VER_MINOR__ * 100ul + __PERF_COUNTER_VER_REVISE__)

/*! @} */

#ifdef __PERF_COUNT_PLATFORM_SPECIFIC_HEADER__
#include __PERF_COUNT_PLATFORM_SPECIFIC_HEADER__
#endif

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wreserved-identifier"
#pragma clang diagnostic ignored "-Wdeclaration-after-statement"
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#pragma clang diagnostic ignored "-Wgnu-statement-expression"
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#pragma clang diagnostic ignored "-Wcompound-token-split-by-macro"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#elif defined(__IS_COMPILER_ARM_COMPILER_5__)
#pragma diag_suppress 550
#elif defined(__IS_COMPILER_GCC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wformat="
#endif

#ifndef __perf_counter_printf__
#define __perf_counter_printf__ printf
#endif

#if __PLOOC_VA_NUM_ARGS() != 0
#warning Please enable GNU extensions, it is required by __cycleof__() and \
__super_loop_monitor__()
#endif

#if defined(__PERF_COUNTER_CFG_USE_SYSTICK_WRAPPER__)
#if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__
#pragma import(__ensure_systick_wrapper)
#elif (defined(__GNUC__) || defined(__clang__)) && \
    (!defined(__IS_COMPILER_IAR__) || !__IS_COMPILER_IAR__)
__asm(".global __ensure_systick_wrapper\n\t");
#endif
#endif
/*! @} */

/*============================ MACROFIED FUNCTIONS ===========================*/

/*!
 * \addtogroup gBasic 1 Basic
 * @{
 */

/*!
 * \brief measure the cycle count of a given code segment
 * \param[in] __STR a description string for the measurement
 * \param[in] ... an optional code segement, in which we can read the measured
 *                result from __cycle_count__.
 * \details Here is an example:
    E.g.
    \code
        __cycleof__("printf") {
            printf("hello world\r\n");
        }
    \endcode
 */
#define __cycleof__(__STR, ...)                                             \
    using(int64_t _ = get_system_ticks(), __cycle_count__ = _, _ = _, {     \
        _ = get_system_ticks() - _ - g_nOffset;                             \
        __cycle_count__ = _;                                                \
        if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) {                        \
            __perf_counter_printf__("\r\n");                                \
            __perf_counter_printf__("-[Cycle Report]");                     \
            __perf_counter_printf__(                                        \
                "--------------------------------------------\r\n");        \
            __perf_counter_printf__(__STR                                   \
                                    " total cycle count: %ld [%016lx]\r\n", \
                                    (long)_, (long)_);                      \
        } else {                                                            \
            __VA_ARGS__                                                     \
        };                                                                  \
    })

/*!
 * \brief measure the cpu usage for a given code segment and print out the
 *        result in percentage.
 * \param[in] __CNT generate result on every given iterations
 * \param[in] ... an optional code segement, in which we can read the measured
 *                result from __usage__ which is a float value.
 * \details Here is an example, 50% cpu time:
    E.g.
    \code
        while (1) {
            __cpu_time__(100) {
                delay_us(5000);
            }
            delay_us(5000);
        }
    \endcode
 */
#define __cpu_time__(__CNT, ...)                                          \
    static int64_t SAFE_NAME(s_lTimestamp) = 0, SAFE_NAME(s_lTotal) = 0;  \
    static uint32_t s_wLoopCounter = (__CNT);                             \
    using(float __usage__ = 0, ({                                         \
              if (0 == s_wLoopCounter) {                                  \
                  __usage__ = (float)((double)SAFE_NAME(s_lTotal) /       \
                                      (double)(get_system_ticks() -       \
                                               SAFE_NAME(s_lTimestamp))); \
                  __usage__ *= 100.0f;                                    \
                  SAFE_NAME(s_lTimestamp) = 0;                            \
                  SAFE_NAME(s_lTotal) = 0;                                \
                  if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) {            \
                      __perf_counter_printf__("CPU Usage %3.2f%%\r\n",    \
                                              (double)__usage_ _);        \
                  } else {                                                \
                      __VA_ARGS__                                         \
                  }                                                       \
              }                                                           \
              if (0 == SAFE_NAME(s_lTimestamp)) {                         \
                  SAFE_NAME(s_lTimestamp) = get_system_ticks();           \
                  s_wLoopCounter = (__CNT);                               \
              }                                                           \
              start_task_cycle_counter();                                 \
          }),                                                             \
          ({                                                              \
              SAFE_NAME(s_lTotal) += stop_task_cycle_counter();           \
              s_wLoopCounter--;                                           \
          }))

/*!
 * \addtogroup gBasicTimerService 1.2 Timer Service
 * \ingroup gBasic
 * @{
 */

/*!
 * \brief should not use
 */
#define perfc_is_time_out_ms0() true

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 * \param[in] __auto_reload whether starting next period after a timeout event
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_ms3(__ms, __timestamp_ptr, __auto_reload) \
    ({                                                              \
        static int64_t SAFE_NAME(s_lTimestamp);                     \
        (void)SAFE_NAME(s_lTimestamp);                              \
        __perfc_is_time_out(perfc_convert_ms_to_ticks(__ms),        \
                            (__timestamp_ptr), (__auto_reload));    \
    })

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_ms2(__ms, __timestamp_ptr) \
    perfc_is_time_out_ms3((__ms), (__timestamp_ptr), true)

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_ms1(__ms) \
    perfc_is_time_out_ms3((__ms), &SAFE_NAME(s_lTimestamp), true)

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] ... an optional timestamp holder
 * \param[in] ... an optional indicator for whether starting next period after a
 * timeout event
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_ms(...)                                    \
    CONNECT2(perfc_is_time_out_ms, __PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \
    (__VA_ARGS__)

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 * \param[in] __auto_reload whether starting next period after a timeout event
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_us3(__us, __timestamp_ptr, __auto_reload) \
    ({                                                              \
        static int64_t SAFE_NAME(s_lTimestamp);                     \
        (void)SAFE_NAME(s_lTimestamp);                              \
        __perfc_is_time_out(perfc_convert_us_to_ticks(__us),        \
                            (__timestamp_ptr), (__auto_reload));    \
    })

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_us2(__us, __timestamp_ptr) \
    perfc_is_time_out_us3((__us), (__timestamp_ptr), true)

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_us1(__us) \
    perfc_is_time_out_us3((__us), &SAFE_NAME(s_lTimestamp), true)

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] ... an optional timestamp holder
 * \param[in] ... an optional indicator for whether starting next period after a
 * timeout event
 *
 * \return bool whether it is timeout
 */
#define perfc_is_time_out_us(...)                                    \
    CONNECT2(perfc_is_time_out_us, __PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \
    (__VA_ARGS__)

/*! @} */

/*! @} */

// clang-format off
/*!
 * \addtogroup gRTOS 2 RTOS Support
 * @{
 */
#define __super_loop_monitor__(__N, ...)                                        \
    using(                                                                      \
        struct {                                                                \
            int64_t lStart;                                                     \
            int64_t lTaskUsedCycles;                                            \
            int64_t lTimeElapsed;                                               \
        } __cpu_usage__ = {.lStart = get_system_ticks()})                       \
    using(int SAFE_NAME(cnt) = (__N))                                           \
    for(start_task_cycle_counter();; ({                                         \
        if (!(--SAFE_NAME(cnt))) {                                              \
            __cpu_usage__.lTimeElapsed                                          \
                = get_system_ticks() - __cpu_usage__.lStart - g_nOffset;        \
            __cpu_usage__.lTaskUsedCycles = stop_task_cycle_counter();          \
                                                                                \
            if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) {                        \
                __perf_counter_printf__(                                        \
                    "%s CPU Usage %2.3f%%\r\n", __func__,                       \
                    (float)((double)__cpu_usage__.lTaskUsedCycles * 100.0 /     \
                            (double)__cpu_usage__.lTimeElapsed));               \
            } else {                                                            \
                __VA_ARGS__;                                                    \
            }                                                                   \
            SAFE_NAME(cnt) = (__N);                                             \
            __cpu_usage__.lStart = get_system_ticks();                          \
            start_task_cycle_counter();                                         \
        };                                                                      \
    }))

// clang-format on

/*============================ TYPES =========================================*/
typedef struct {
    int64_t lStart;
    int64_t lUsedTotal;
    int32_t nUsedRecent;
    uint16_t hwActiveCount;
    uint16_t : 15;
    uint16_t bEnabled : 1;
} task_cycle_info_t;

typedef struct task_cycle_info_agent_t task_cycle_info_agent_t;

struct task_cycle_info_agent_t {
    task_cycle_info_t* ptInfo;
    task_cycle_info_agent_t* ptNext;
    task_cycle_info_agent_t* ptPrev;
};

/*! @} */

/*============================ GLOBAL VARIABLES ==============================*/
extern volatile int64_t g_lLastTimeStamp;
extern volatile int32_t g_nOffset;
/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/

/*!
 * \addtogroup gBasicTicks 1.1 Ticks APIs
 * \ingroup gBasic
 * @{
 */
/*!
 * \brief get the elapsed cycles since perf_counter is initialised
 * \return int64_t the elpased cycles
 */
__attribute__((noinline)) extern int64_t get_system_ticks(void);

#ifdef __PERF_CNT_USE_LONG_CLOCK__
/*! \note the prototype of this clock() is different from the one defined in
 *!           time.h. As clock_t is usually defined as unsigned int, it is
 *!           not big enough in Cortex-M system to hold a time-stamp. clock()
 *!           defined here returns the timestamp since the begining of main()
 *!           and its unit is clock cycle (rather than 1ms). Hence, for a system
 *!           running under several hundreds MHz or even 1GHz, e.g. RT10xx from
 *!           NXP, it is very easy to see a counter overflow as clock_t is
 *!           defined as uint32_t in timer.h.
 *!           Since we are not allowed to change the defintion of clock_t in
 *!           official header file, i.e. time.h, I use a compatible prototype
 *!           after I checked the AAPCS spec. So, the return of the clock() is
 *!           int64_t, which will use the R0 to store the lower 32bits and R1
 *!           to store the higher 32bits. When you are using the prototype from
 *!           timer.h, caller will only take the lower 32bits stored in R0 and
 *!           the higher 32bits stored in R1 will be ignored.
 *!
 *!           If you want to use the non-overflow version of this clock(),
 *please !           1) define the MACRO: __PERF_CNT_USE_LONG_CLOCK__ in your
 *project !           and 2) do not include system header file <time.h>
 *!
 */
#if !defined(__IS_COMPILER_IAR__)
__attribute__((nothrow))
#endif
__attribute__((noinline)) extern int64_t
clock(void);
#endif

/*!
 * \brief try to set a start pointer for the performance counter
 */
__STATIC_INLINE
void start_cycle_counter(void) {
    g_lLastTimeStamp = get_system_ticks();
}

/*!
 * \brief calculate the elapsed cycle count since the last start point
 * \note  you can have multiple stop_cycle_counter following one start point
 * \return int32_t the elapsed cycle count
 */
__STATIC_INLINE
int64_t stop_cycle_counter(void) {
    int64_t lTemp = (get_system_ticks() - g_lLastTimeStamp);

    return lTemp - g_nOffset;
}

/*! @} */

/*!
 * \addtogroup gBasicTimerService 1.2 Timer Service
 * \ingroup gBasic
 * @{
 */

/*!
 * \brief get the elapsed milliseconds since perf_counter is initialised
 * \return int32_t the elapsed milliseconds
 */
extern int32_t get_system_ms(void);

/*!
 * \brief get the elapsed microsecond since perf_counter is initialised
 * \return int32_t the elapsed microsecond
 */
extern int32_t get_system_us(void);

/*!
 * \brief delay specified time in microsecond
 * \param[in] nUs time in microsecond
 */
extern void delay_us(int32_t nUs);

/*!
 * \brief delay specified time in millisecond
 * \param[in] nMs time in millisecond
 */
extern void delay_ms(int32_t nMs);

/*!
 * \brief convert ticks of a reference timer to millisecond
 *
 * \param[in] lTick the tick count
 * \return int64_t the millisecond
 */
extern int64_t perfc_convert_ticks_to_ms(int64_t lTick);

/*!
 * \brief convert millisecond into ticks of the reference timer
 *
 * \param[in] wMS the target time in millisecond
 * \return int64_t the ticks
 */
extern int64_t perfc_convert_ms_to_ticks(uint32_t wMS);

/*!
 * \brief convert ticks of a reference timer to microsecond
 *
 * \param[in] lTick the tick count
 * \return int64_t the microsecond
 */
extern int64_t perfc_convert_ticks_to_us(int64_t lTick);

/*!
 * \brief convert microsecond into ticks of the reference timer
 *
 * \param[in] wUS the target time in microsecond
 * \return int64_t the ticks
 */
extern int64_t perfc_convert_us_to_ticks(uint32_t wUS);

/*!
 * \brief set an alarm with given period and check the status
 *
 * \param[in] lPeriod a time period in ticks
 * \param[in] plTimestamp a pointer points to an int64_t integer, if NULL is
 *            passed, an static local variable inside the function will be used
 * \param[in] bAutoReload whether starting next period after a timeout event.
 * \return bool whether it is timeout or not
 */
extern bool __perfc_is_time_out(int64_t lPeriod, int64_t* plTimestamp,
                                bool bAutoReload);

/*! @} */

/*!
 * \addtogroup gRTOS 2 RTOS Support
 * @{
 */

#if defined(__PERF_CNT_USE_RTOS__)

/*! \brief initialize the default virtual cycle counter for the current task
 */
extern void init_task_cycle_counter(void);

/*! \brief check whether the task stack canary is safe or not
 *  \retval false likely to be a stack-overflow
 *  \retval true task stack is safe
 */
extern bool perfc_check_task_stack_canary_safe(void);

/*! \brief provide cycle information for target task
 *  \details Support RTOS List:
 *           - RTX5
 *           - RT-Thread
 *           - ThreadX
 *           - FreeRTOS
 *
 * \return task_cycle_info_t* the cycle info object passed to this function
 */
extern task_cycle_info_t* get_rtos_task_cycle_info(void);

/*!
 * \brief intialize a given task_cycle_info_t object and enable it before
 *        registering it.
 * \return task_cycle_info_t* the cycle info object passed to this function
 */
extern task_cycle_info_t* init_task_cycle_info(task_cycle_info_t* ptInfo);

/*! \brief enable a given task_cycle_info_t object
 *
 * \param[in] ptInfo the address of target task_cycle_info_t object
 * \return bool previous status
 */
extern bool enable_task_cycle_info(task_cycle_info_t* ptInfo);

/*! \brief disable a given task_cycle_info_t object
 *
 * \param[in] ptInfo the address of target task_cycle_info_t object
 * \return bool previous status
 */
extern bool disable_task_cycle_info(task_cycle_info_t* ptInfo);

/*! \brief resume the enabled status of a given task_cycle_info_t object
 *
 * \param[in] ptInfo the address of target task_cycle_info_t object
 * \param[in] bEnabledStatus the previous status
 */
extern void resume_task_cycle_info(task_cycle_info_t* ptInfo,
                                   bool bEnabledStatus);

/*!
 * \brief register a global virtual cycle counter agent to the current task
 * \param[in] ptInfo the address of target task_cycle_info_t object
 * \param[in] ptAgent an list node for the task_cycle_info_t object
 * \note the ptAgent it is better to be allocated as a static variable, global
 *       variable or comes from heap or pool
 *
 * \return task_cycle_info_agent_t* the agent passed to this function
 */
extern task_cycle_info_agent_t* register_task_cycle_agent(
    task_cycle_info_t* ptInfo, task_cycle_info_agent_t* ptAgent);

/*!
 * \brief remove a global virtual cycle counter agent from the current task
 * \param[in] ptAgent the list node currently in use
 * \return task_cycle_info_agent_t* the agent passed to this function
 */
extern task_cycle_info_agent_t* unregister_task_cycle_agent(
    task_cycle_info_agent_t* ptAgent);

/*! \brief reset and start the virtual cycle counter for the current task
 *
 * \param[in] ptInfo the target task_cycle_info_t object
 */
__attribute__((noinline)) extern void __start_task_cycle_counter(
    task_cycle_info_t* ptInfo);

/*! \brief calculate the elapsed cycle count for current task since the last
 *        start point
 *
 * \note you can call stop_cycle_counter() multiple times following one
 *       start_task_cycle_counter()
 *
 * \param[in] ptInfo the target task_cycle_info_t object
 *
 * \note  When ptInfo is NULL, it returns current task cycle info, when ptInfo
 *        is non-NULL, it returns the total used cycles of the specified
 *        task_cycle_info_t object.
 *
 * \return int64_t the elapsed cycle count.
 */
__attribute__((noinline)) extern int64_t __stop_task_cycle_counter(
    task_cycle_info_t* ptInfo);

#define start_task_cycle_counter(...) \
    __start_task_cycle_counter((NULL, ##__VA_ARGS__))

#define stop_task_cycle_counter(...) \
    __stop_task_cycle_counter((NULL, ##__VA_ARGS__))

#elif !defined(__IMPLEMENT_PERF_COUNTER)
#define start_task_cycle_counter(...) start_cycle_counter()
#define stop_task_cycle_counter(...) stop_cycle_counter()
#define init_task_cycle_counter()
#define register_task_cycle_agent(...)
#define unregister_task_cycle_agent(...)
#define init_task_cycle_info(...) (NULL)
#define enable_task_cycle_info(...) (false)
#define disable_task_cycle_info(...) (false)
#define resume_task_cycle_info(...)
#define perfc_check_task_stack_canary_safe() (false)
#endif

/*! @} */

/*!
 * \addtogroup gBasic 1 Basic
 * @{
 */

/*----------------------------------------------------------------------------*
 * Please ignore the following APIs unless you have encountered some known    *
 * special conditions                                                         *
 *----------------------------------------------------------------------------*/

/*! \brief   initialise cycle counter service
 *  \note    - don't forget to tell the function whether the systick is already
 *           used by user applications.
 *           Don't worry, this cycle counter service won't affect your existing
 *           systick service.
 *
 *  \note    - Usually the perf_counter can initialise itself with the help of
 *           __attribute__((constructor(255))), this works fine in Arm Compiler
 *           5 (armcc), Arm Compiler 6 (armclang), arm gcc and llvm. It doesn't
 *           work for IAR. So, when you are using IAR, please call this function
 *           manually to initialise the perf_counter service.
 *
 *  \note    - Perf_counter library assumes that:
 *           1. Your project has already using SysTick
 *           2. It assumes that you have already implemented the SysTick_Handler
 *           3. It assumes that you have enabled the exception handling for
 *              SysTick.
 *           If these are not the case, please:
 *               1. Add an empty SysTick_Handler to your project if you don't
 * have one
 *               2. Make sure you have the SysTick Exception handling enabled
 *               3. And call function init_cycle_counter(false) if you doesn't
 *              use SysTick in your project at all.
 *
 *  \param[in] bIsSysTickOccupied  A boolean value which indicates whether
 * SysTick is already used by user application.
 */
extern void init_cycle_counter(bool bIsSysTickOccupied);

/*!
 * \brief a system timer handler inserted to the SysTick_Handler
 *
 * \note  - if you are using a compiler other than armcc or armclang, e.g. iar,
 *        arm gcc etc, the systick_wrapper_ual.o doesn't work with the linker
 *        of your target toolchain as it use the $Super$$ which is only
 * supported by armlink. For this condition, you have to manually put this
 * function into your existing SysTick_Handler to make the perf_counter library
 *        work.
 *
 * \note  - if you are using Arm Compiler 5 (armcc) or Arm Compiler 6 (armclang)
 *        you do NOT have to insert this function into your SysTick_Handler,
 *        the systick_wrapper_ual.s will do the work for you.
 */
extern void user_code_insert_to_systick_handler(void);

/*!
 * \brief update perf_counter as SystemCoreClock has been updated.
 */
extern void update_perf_counter(void);

/*!
 * \brief prepare for reconfiguration of SysTick timer.
 *
 * \note some systems (e.g. FreeRTOS) might reconfigure the systick timer to
 *       fulfil the requirement of their feature. To support this, just
 *       before the reconfiguration, please call this function in order
 *       to make the perf_counter works correctly later.
 *
 * \note after the reconfiguration, please call update_perf_counter() to apply
 *       the changes to perf_counter.
 *
 * \note this function will stop the SysTick, clear the pending bit and set
 *       the Load register and Current Value register to zero.
 */
extern void before_cycle_counter_reconfiguration(void);

/*! @} */

/*!
 * \addtogroup gBenchmark 3 Benchmark
 * @{
 */

#ifdef __PERF_COUNTER_COREMARK__

/*!
 * \brief entry for coremark
 */
void coremark_main(void);
#endif

/*! @} */

// #if defined(__clang__)
// #   pragma clang diagnostic pop
// #elif defined(__IS_COMPILER_GCC__)
// #   pragma GCC diagnostic pop
// #endif

#ifdef __cplusplus
}
#endif

#endif /* __PERF_COUNTER_H__ */
